/**
 * WYSIWYG - jQuery plugin 0.5
 * 
 * Copyright (c) 2008-2009 Juan M Martinez
 * http://plugins.jquery.com/project/jWYSIWYG
 * 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * 
 * $Id: $
 */
(function($) {
	$.fn.document = function() {
		var element = this[0];

		if (element.nodeName.toLowerCase() == 'iframe')
			return element.contentWindow.document;
		/*
		 * return ( $.browser.msie ) ? document.frames[element.id].document :
		 * element.contentWindow.document // contentDocument;
		 */
		else
			return $(this);
	};

	$.fn.documentSelection = function() {
		var element = this[0];

		if (element.contentWindow.document.selection)
			return element.contentWindow.document.selection.createRange().text;
		else
			return element.contentWindow.getSelection().toString();
	};

	$.fn.wysiwyg = function(options) {
		if (arguments.length > 0 && arguments[0].constructor == String) {
			var action = arguments[0].toString();
			var params = [];

			for (var i = 1; i < arguments.length; i++)
				params[i - 1] = arguments[i];

			if (action in Wysiwyg) {
				return this.each(function() {
							$.data(this, 'wysiwyg').designMode();

							Wysiwyg[action].apply(this, params);
						});
			} else
				return this;
		}

		var controls = {};

		/**
		 * If the user set custom controls, we catch it, and merge with the
		 * defaults controls later.
		 */
		if (options && options.controls) {
			var controls = options.controls;
			delete options.controls;
		}

		var options = $.extend({
			html : '<'
					+ '?xml version="1.0" encoding="UTF-8"?'
					+ '><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">STYLE_SHEET</head><body style="font-family: Arial, Helvetica, sans-serif !important; font-size: 13px; height: 100%; line-height: 1.5em !important;">INITIAL_CONTENT</body></html>',
			css : {},

			debug : false,

			autoSave : true, // http://code.google.com/p/jwysiwyg/issues/detail?id=11
			rmUnwantedBr : true, // http://code.google.com/p/jwysiwyg/issues/detail?id=15
			brIE : true,

			controls : {},
			messages : {}
		}, options);

		options.messages = $.extend(true, options.messages, Wysiwyg.MSGS_EN);
		options.controls = $.extend(true, options.controls, Wysiwyg.TOOLBAR);

		for (var control in controls) {
			if (control in options.controls)
				$.extend(options.controls[control], controls[control]);
			else
				options.controls[control] = controls[control];
		}

		// not break the chain
		return this.each(function() {
					Wysiwyg(this, options);
				});
	};

	function Wysiwyg(element, options) {
		return this instanceof Wysiwyg
				? this.init(element, options)
				: new Wysiwyg(element, options);
	}

	$.extend(Wysiwyg, {
		insertImage : function(szURL, attributes) {
			var self = $.data(this, 'wysiwyg');

			if (self.constructor == Wysiwyg && szURL && szURL.length > 0) {
				if (attributes) {
					self.editorDoc.execCommand('insertImage', false,
							'#jwysiwyg#');
					var img = self.getElementByAttributeValue('img', 'src',
							'#jwysiwyg#');

					if (img) {
						img.src = szURL;

						for (var attribute in attributes) {
							img.setAttribute(attribute, attributes[attribute]);
						}
					}
				} else {
					self.editorDoc.execCommand('insertImage', false, szURL);
				}
			}
		},

		createLink : function(szURL) {
			var self = $.data(this, 'wysiwyg');

			if (self.constructor == Wysiwyg && szURL && szURL.length > 0) {
				var selection = $(self.editor).documentSelection();

				if (selection.length > 0) {
					self.editorDoc.execCommand('unlink', false, []);
					self.editorDoc.execCommand('createLink', false, szURL);
				} else if (self.options.messages.nonSelection)
					alert(self.options.messages.nonSelection);
			}
		},

		setContent : function(newContent) {
			var self = $.data(this, 'wysiwyg');
			self.setContent(newContent);
			self.saveContent();
		},

		clear : function() {
			var self = $.data(this, 'wysiwyg');
			self.setContent('');
			self.saveContent();
		},

		MSGS_EN : {
			nonSelection : 'select the text you wish to link'
		},

		TOOLBAR : {
			bold : {
				visible : true,
				tags : ['b', 'strong'],
				css : {
					fontWeight : 'bold'
				}
			},
			italic : {
				visible : true,
				tags : ['i', 'em'],
				css : {
					fontStyle : 'italic'
				}
			},
			strikeThrough : {
				visible : false,
				tags : ['s', 'strike'],
				css : {
					textDecoration : 'line-through'
				}
			},
			underline : {
				visible : false,
				tags : ['u'],
				css : {
					textDecoration : 'underline'
				}
			},

			separator00 : {
				visible : false,
				separator : true
			},

			justifyLeft : {
				visible : false,
				css : {
					textAlign : 'left'
				}
			},
			justifyCenter : {
				visible : false,
				tags : ['center'],
				css : {
					textAlign : 'center'
				}
			},
			justifyRight : {
				visible : false,
				css : {
					textAlign : 'right'
				}
			},
			justifyFull : {
				visible : false,
				css : {
					textAlign : 'justify'
				}
			},

			separator01 : {
				visible : false,
				separator : true
			},

			indent : {
				visible : false
			},
			outdent : {
				visible : false
			},

			separator02 : {
				visible : false,
				separator : true
			},

			subscript : {
				visible : false,
				tags : ['sub']
			},
			superscript : {
				visible : false,
				tags : ['sup']
			},

			separator03 : {
				visible : false,
				separator : true
			},

			undo : {
				visible : false
			},
			redo : {
				visible : false
			},

			separator04 : {
				visible : false,
				separator : true
			},

			insertOrderedList : {
				visible : false,
				tags : ['ol']
			},
			insertUnorderedList : {
				visible : false,
				tags : ['ul']
			},
			insertHorizontalRule : {
				visible : false,
				tags : ['hr']
			},

			separator05 : {
				separator : true
			},

			createLink : {
				visible : true,
				exec : function() {
					var selection = $(this.editor).documentSelection();

					if (selection.length > 0) {
						if ($.browser.msie)
							this.editorDoc
									.execCommand('createLink', true, null);
						else {
							var szURL = prompt('URL', 'http://');

							if (szURL && szURL.length > 0) {
								this.editorDoc.execCommand('unlink', false, []);
								this.editorDoc.execCommand('createLink', false,
										szURL);
							}
						}
					} else if (this.options.messages.nonSelection)
						alert(this.options.messages.nonSelection);
				},

				tags : ['a']
			},

			insertImage : {
				visible : true,
				exec : function() {
					if ($.browser.msie)
						this.editorDoc.execCommand('insertImage', true, null);
					else {
						var szURL = prompt('URL', 'http://');

						if (szURL && szURL.length > 0)
							this.editorDoc.execCommand('insertImage', false,
									szURL);
					}
				},

				tags : ['img']
			},

			separator06 : {
				separator : true
			},

			h1mozilla : {
				visible : true && $.browser.mozilla,
				className : 'h1',
				command : 'heading',
				arguments : ['h1'],
				tags : ['h1']
			},
			h2mozilla : {
				visible : true && $.browser.mozilla,
				className : 'h2',
				command : 'heading',
				arguments : ['h2'],
				tags : ['h2']
			},
			h3mozilla : {
				visible : true && $.browser.mozilla,
				className : 'h3',
				command : 'heading',
				arguments : ['h3'],
				tags : ['h3']
			},

			h1 : {
				visible : true && !($.browser.mozilla),
				className : 'h1',
				command : 'formatBlock',
				arguments : ['Heading 1'],
				tags : ['h1']
			},
			h2 : {
				visible : true && !($.browser.mozilla),
				className : 'h2',
				command : 'formatBlock',
				arguments : ['Heading 2'],
				tags : ['h2']
			},
			h3 : {
				visible : true && !($.browser.mozilla),
				className : 'h3',
				command : 'formatBlock',
				arguments : ['Heading 3'],
				tags : ['h3']
			},

			separator07 : {
				visible : false,
				separator : true
			},

			cut : {
				visible : false
			},
			copy : {
				visible : false
			},
			paste : {
				visible : false
			},

			separator08 : {
				separator : true && !($.browser.msie)
			},

			increaseFontSize : {
				visible : true && !($.browser.msie),
				tags : ['big']
			},
			decreaseFontSize : {
				visible : true && !($.browser.msie),
				tags : ['small']
			},

			separator09 : {
				separator : true
			},

			html : {
				visible : false,
				exec : function() {
					if (this.viewHTML) {
						this.setContent($(this.original).val());
						$(this.original).hide();
					} else {
						this.saveContent();
						$(this.original).show();
					}

					this.viewHTML = !(this.viewHTML);
				}
			},

			removeFormat : {
				visible : true,
				exec : function() {
					this.editorDoc.execCommand('removeFormat', false, []);
					this.editorDoc.execCommand('unlink', false, []);
				}
			}
		}
	});

	$.extend(Wysiwyg.prototype, {
		original : null,
		options : {},

		element : null,
		editor : null,

		init : function(element, options) {
			var self = this;

			this.editor = element;
			this.options = options || {};

			$.data(element, 'wysiwyg', this);

			var newX = element.width || element.clientWidth;
			var newY = element.height || element.clientHeight;

			if (element.nodeName.toLowerCase() == 'textarea') {
				this.original = element;

				if (newX == 0 && element.cols)
					newX = (element.cols * 8) + 21;

				if (newY == 0 && element.rows)
					newY = (element.rows * 16) + 16;

				var editor = this.editor = $('<iframe></iframe>').css({
							minHeight : (newY - 6).toString() + 'px',
							width : (newX - 8).toString() + 'px'
						}).attr('id', $(element).attr('id') + 'IFrame');

				if ($.browser.msie) {
					this.editor.css('height', (newY).toString() + 'px');

					/**
					 * var editor = $('<span></span>').css({ width : ( newX -
					 * 6 ).toString() + 'px', height : ( newY - 8 ).toString() +
					 * 'px' }).attr('id', $(element).attr('id') + 'IFrame');
					 * 
					 * editor.outerHTML = this.editor.outerHTML;
					 */
				}
			}

			var panel = this.panel = $('<ul></ul>').addClass('panel');

			this.appendControls();
			this.element = $('<div></div>').css({
						width : (newX > 0) ? (newX).toString() + 'px' : '100%'
					}).addClass('wysiwyg').append(panel)
					.append($('<div><!-- --></div>').css({
								clear : 'both'
							})).append(editor);

			$(element)
					// .css('display', 'none')
					.hide().before(this.element);

			this.viewHTML = false;

			this.initialHeight = newY - 8;

			/**
			 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=52
			 */
			this.initialContent = $(element).val();

			this.initFrame();

			if (this.initialContent.length == 0)
				this.setContent('');

			if (this.options.autoSave)
				$('form').submit(function() {
							self.saveContent();
						});

			$('form').bind('reset', function() {
						self.setContent(self.initialContent);
						self.saveContent();
					});
		},

		initFrame : function() {
			var self = this;
			var style = '';

			/**
			 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=14
			 */
			if (this.options.css && this.options.css.constructor == String)
				style = '<link rel="stylesheet" type="text/css" media="screen" href="'
						+ this.options.css + '" />';

			this.editorDoc = $(this.editor).document();
			this.editorDoc_designMode = false;

			try {
				this.editorDoc.designMode = 'on';
				this.editorDoc_designMode = true;
			} catch (e) {
				// Will fail on Gecko if the editor is placed in an hidden
				// container element
				// The design mode will be set ones the editor is focused

				$(this.editorDoc).focus(function() {
							self.designMode();
						});
			}

			this.editorDoc.open();
			this.editorDoc.write(this.options.html.replace(/INITIAL_CONTENT/,
							this.initialContent).replace(/STYLE_SHEET/, style));
			this.editorDoc.close();
			this.editorDoc.contentEditable = 'true';

			if ($.browser.msie) {
				/**
				 * Remove the horrible border it has on IE.
				 */
				setTimeout(function() {
							$(self.editorDoc.body).css('border', 'none');
						}, 0);
			}

			$(this.editorDoc).click(function(event) {
				self.checkTargets(event.target
						? event.target
						: event.srcElement);
			});

			/**
			 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=20
			 */
			$(this.original).focus(function() {
						$(self.editorDoc.body).focus();
					});

			if (this.options.autoSave) {
				/**
				 * @link http://code.google.com/p/jwysiwyg/issues/detail?id=11
				 */
				$(this.editorDoc).keydown(function() {
							self.saveContent();
						}).keyup(function() {
							self.saveContent();
						}).mousedown(function() {
							self.saveContent();
						});
			}

			if (this.options.css) {
				setTimeout(function() {
							if (self.options.css.constructor == String) {
								/**
								 * $(self.editorDoc) .find('head') .append( $('<link
								 * rel="stylesheet" type="text/css"
								 * media="screen" />') .attr('href',
								 * self.options.css) );
								 */
							} else
								$(self.editorDoc).find('body')
										.css(self.options.css);
						}, 0);
			}

			$(this.editorDoc).keydown(function(event) {
				if ($.browser.msie && self.options.brIE && event.keyCode == 13) {
					var rng = self.getRange();
					rng.pasteHTML('<br />');
					rng.collapse(false);
					rng.select();

					return false;
				}
			});
		},

		designMode : function() {
			if (!(this.editorDoc_designMode)) {
				try {
					this.editorDoc.designMode = 'on';
					this.editorDoc_designMode = true;
				} catch (e) {
				}
			}
		},

		getSelection : function() {
			return (window.getSelection)
					? window.getSelection()
					: document.selection;
		},

		getRange : function() {
			var selection = this.getSelection();

			if (!(selection))
				return null;

			return (selection.rangeCount > 0)
					? selection.getRangeAt(0)
					: selection.createRange();
		},

		getContent : function() {
			return $($(this.editor).document()).find('body').html();
		},

		setContent : function(newContent) {
			$($(this.editor).document()).find('body').html(newContent);
		},

		saveContent : function() {
			if (this.original) {
				var content = this.getContent();

				if (this.options.rmUnwantedBr)
					content = (content.substr(-4) == '<br>') ? content.substr(
							0, content.length - 4) : content;

				$(this.original).val(content);
			}
		},

		appendMenu : function(cmd, args, className, fn) {
			var self = this;
			var args = args || [];

			$('<li></li>').append($('<a><!-- --></a>').addClass(className
							|| cmd)).mousedown(function() {
						if (fn)
							fn.apply(self);
						else
							self.editorDoc.execCommand(cmd, false, args);
						if (self.options.autoSave)
							self.saveContent();
					}).appendTo(this.panel);
		},

		appendMenuSeparator : function() {
			$('<li class="separator"></li>').appendTo(this.panel);
		},

		appendControls : function() {
			for (var name in this.options.controls) {
				var control = this.options.controls[name];

				if (control.separator) {
					if (control.visible !== false)
						this.appendMenuSeparator();
				} else if (control.visible) {
					this.appendMenu(control.command || name, control.arguments
									|| [], control.className || control.command
									|| name || 'empty', control.exec);
				}
			}
		},

		checkTargets : function(element) {
			for (var name in this.options.controls) {
				var control = this.options.controls[name];
				var className = control.className || control.command || name
						|| 'empty';

				$('.' + className, this.panel).removeClass('active');

				if (control.tags) {
					var elm = element;

					do {
						if (elm.nodeType != 1)
							break;

						if ($.inArray(elm.tagName.toLowerCase(), control.tags) != -1)
							$('.' + className, this.panel).addClass('active');
					} while (elm = elm.parentNode);
				}

				if (control.css) {
					var elm = $(element);

					do {
						if (elm[0].nodeType != 1)
							break;

						for (var cssProperty in control.css)
							if (elm.css(cssProperty).toString().toLowerCase() == control.css[cssProperty])
								$('.' + className, this.panel)
										.addClass('active');
					} while (elm = elm.parent());
				}
			}
		},

		getElementByAttributeValue : function(tagName, attributeName,
				attributeValue) {
			var elements = this.editorDoc.getElementsByTagName(tagName);

			for (var i = 0; i < elements.length; i++) {
				var value = elements[i].getAttribute(attributeName);

				if ($.browser.msie) {
					/** IE add full path, so I check by the last chars. */
					value = value.substr(value.length - attributeValue.length);
				}

				if (value == attributeValue)
					return elements[i];
			}

			return false;
		}
	});
})(jQuery);